<?php

class TokenBucket
{
    // 最大限制
    const MAX_LIMIT = 1000;
    // 限制单位时间内最多跑多少次
    const LIMIT_COUNT = 100;

    // 多少时间间隔
    const LIMIT_UNIT = 60;

    // REDIS string的键
    const LIMIT_KEY = 'limit:token:';

    const MAX_TOKEN = 10;

    // 过期时间
    const TTL = 30;

    private $redis;

    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }


    public function checkRequest($uid)
    {
        $key = self::LIMIT_KEY . $uid;
        $data = $this->redis->get($key);
        // 是否有请求的记录数据
        if ($data) {
            $value = json_decode($data, true);

            // 计算上个请求到现在为止会生成多少个令牌
            $nowTime = microtime(true) * 1000;
            $speed = self::LIMIT_COUNT / (self::LIMIT_UNIT * 1000);

            $timeGap = $nowTime - $value['time'];
            $tokenCount = $timeGap * $speed;

            // 限制一次生成的token最大数量
            $tokenCount = min(self::MAX_TOKEN, $tokenCount);

            // 之前的数量 加上生成的数量 再减掉这次消耗的1个
            $count = ($value['count'] + $tokenCount - 1) % self::MAX_LIMIT;
            // 记录下令牌的变动
            $this->redis->set($key, json_encode($this->getJsonData($count)), self::TTL);

            if ($count <= 0) {
                return false;
            } else {
                return true;
            }

        } else {
            // 设置初始时间信息
            $initData = $this->getJsonData(self::MAX_TOKEN);
            $this->redis->set($key, json_encode($initData), self::TTL);
            return true;
        }
    }

    private function getJsonData($count)
    {
        return [
            'count' => $count,
            'time'  => microtime(true) * 1000
        ];
    }

}